use std::{cell::RefCell, fmt::Debug, rc::Rc};

type Link<T> = Option<Rc<RefCell<Node<T>>>>;

#[derive(Debug)]
pub struct Node<T>
where
    T: Debug,
{
    pub value: T,
    pub next: Link<T>,
}

#[derive(Debug)]
pub struct LinkedList<T>
where
    T: Debug,
{
    pub head: Link<T>,
    pub tail: Link<T>,
}

impl<T> Node<T>
where
    T: Debug,
{
    pub fn new(value: T) -> Self {
        Node { value, next: None }
    }
}

impl<T> LinkedList<T>
where
    T: Debug,
{
    pub fn new() -> Self {
        LinkedList {
            head: None,
            tail: None,
        }
    }

    pub fn push_front(&mut self, value: T) {
        let node = Rc::new(RefCell::new(Node {
            value,
            next: self.head.take(),
        }));
        if self.tail.is_none() {
            self.tail = Some(Rc::clone(&node));
        }
        self.head = Some(node);
    }

    pub fn push_back(&mut self, value: T) {
        let node = Rc::new(RefCell::new(Node::new(value)));
        match self.tail.as_mut() {
            None => {
                self.tail = Some(Rc::clone(&node));
                self.head = Some(node);
            }
            Some(d) => {
                d.borrow_mut().next = Some(Rc::clone(&node));
                *d = node;
            }
        };
    }

    pub fn pop_front(&mut self) -> Option<T> {
        let first_link = self.head.take();
        let next_link = match first_link.as_ref() {
            Some(x) => x.borrow_mut().next.take(),
            None => None,
        };
        self.head = next_link;
        if self.head.is_none() {
            self.tail = None;
        }
        first_link.map(|r| Rc::try_unwrap(r).ok().unwrap().into_inner().value)
    }

    pub fn pop_back(&mut self) -> Option<T> {
        //let last = self.tail.take();

        None
    }
}

#[cfg(test)]
mod test {

    use super::LinkedList;

    #[test]
    fn test() {
        // let mut list = LinkedList::new();
        // list.push_back(1);
        // list.push_back(2);
        // list.push_back(3);
        // assert_eq!(list.pop_back(), Some(3));
        // assert_eq!(list.pop_back(), Some(2));
        // assert_eq!(list.pop_back(), Some(1));
        // assert_eq!(list.pop_back(), None);

        let mut list = LinkedList::new();
        list.push_front(1);
        list.push_front(2);
        list.push_front(3);
        assert_eq!(list.pop_front(), Some(3));
        assert_eq!(list.pop_front(), Some(2));
        assert_eq!(list.pop_front(), Some(1));
        assert_eq!(list.pop_front(), None);

        let mut list = LinkedList::new();
        list.push_back(1);
        list.push_back(2);
        list.push_back(3);
        assert_eq!(list.pop_front(), Some(1));
        assert_eq!(list.pop_front(), Some(2));
        assert_eq!(list.pop_front(), Some(3));
        assert_eq!(list.pop_front(), None);

        // let mut list = LinkedList::new();
        // list.push_front(1);
        // list.push_front(2);
        // list.push_front(3);
        // assert_eq!(list.pop_back(), Some(1));
        // assert_eq!(list.pop_back(), Some(2));
        // assert_eq!(list.pop_back(), Some(3));
        // assert_eq!(list.pop_back(), None);
    }
}
